home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / ThreadLibrary-1.0 / Source / Libraries / ThreadLibrary.c < prev    next >
Text File  |  1995-05-04  |  61KB  |  1,826 lines

  1. /* See the file Distribution for distribution terms.
  2.     (c) Copyright 1994 Ari Halberstadt */
  3.  
  4. /*    Thread Library implements nonpreemptive multiple thread execution within
  5.     a single application.
  6.  
  7.     950403 aih    -    added custom scheduler function
  8.     950328 aih    -    removed user-settable fpu save flag
  9.     950313 aih    -    ported to native ppc
  10.     950220 aih    -    added stack size accessor function
  11.                     -    added memory types for memory allocation callbacks
  12.     950219 aih    -    added sleep interval accessor function
  13.     950214 aih    -    general code review
  14.                     -    added missing check for thread->enabled in calculation
  15.                         of yield interval
  16.     950203 aih    -    removed stack sniffer code
  17.                     -    added memory allocation callbacks
  18.                     -    added register a6 access (this may go away again in the future)
  19.     941207 aih    -    removed almost all compiler dependencies
  20.                     -    added ifdef's to the few (3) places where powerpc-specific
  21.                         code is needed--a native PPC version is around the corner!
  22.     941020 aih    -    added an enabled flag
  23.     941012 aih    -    added an exit function; the exit function is called
  24.                         after the suspend function and before the thread is
  25.                         disposed of
  26.     941007 aih    -    fixed passing wrong data parameter to suspend function.
  27.     940706 aih    -    moved context switching for exceptions and the profiler,
  28.                         which are used in winter shell, out of thread library
  29.                         and into a separate file that will be distributed with
  30.                         winter shell.
  31.     940629 aih    -    added support for universal headers
  32.     940617 aih    -    fixed error in detecting events that was introduced on 940609
  33.                     -    added support for context-switching a profiler
  34.                     -    improved context switches for exceptions (made faster)
  35.                     -    removed status field and operations
  36.                     -    added ThreadErrorType and defined some error codes
  37.                     -    fixed order of execution of actions taken when a thread
  38.                         is resumed (the stack sniffer was being resumed before
  39.                         the low-memory globals were restored)
  40.     940609 aih    -    to prevent context switches from threads other than the
  41.                         main thread, I removed the call to EventAvail, and replaced
  42.                         it with checks of the CurActive/CurDeactive low-memory
  43.                         globals and a call to CheckUpdate.
  44.     940531 aih    -    changed ExceptionType to ExceptionStructure; this change
  45.                         was made for syntactic compatability with the current
  46.                         version of Winter Shell, and has no impact on the
  47.                         functionality of Thread Library.
  48.     940316 aih    -    changed ThreadType to ThreadStructure, and ThreadSNType
  49.                         to ThreadType.
  50.     940311 aih    -    made gThread and gStackSniffer static variables
  51.     940309 aih    -    in v1.0d3 i wrote the stack sniffer sentinel value right over
  52.                         the heap zone information for the application heap. this of
  53.                         course resulted in a damaged heap. what a stupid mistake.
  54.                         now the main thread doesn't use a stack sniffer sentinel
  55.                         value, but all other threads still use the stack sniffer
  56.                         sentinel value.
  57.     9403?? aih    -    release 1.0d3
  58.     940228 aih    -    fixed error in setting thread error code in ThreadFromSN
  59.                     -    added sentinel value to stack sniffer VBL task
  60.     940225 aih    -    will compile with either "LoMem.h" or "SysEqu.h"
  61.     940223 aih    -    fixed an error in the private routine ThreadStackFrame
  62.     940219 aih    -    added ThreadSleepSet, ThreadData, ThreadDataSet
  63.                     -    added doubly-linked list circular queue of threads
  64.                     -    all external functions refer to threads using thread serial
  65.                          numbers instead of thread pointers
  66.                     -    when a thread is activated it is moved to the end of the
  67.                          queue of threads, so that the round-robbin scheduling is
  68.                          fairer (since the main thread has the highest priority).
  69.     940218 aih    -    release 1.0d2.1
  70.                     -    to reduce the possibility of conflict with user-defined types,
  71.                          uses ThreadTicksType instead of TicksType, THREAD_TICKS_MAX
  72.                          instead of TICKS_MAX, and THREAD_TICKS_SEC instead of
  73.                          TICKS_SEC
  74.                     -    includes actual headers instead of using THINK C's
  75.                          non-standard MacHeaders. this was done primarily so I
  76.                          could use "SysEqu.h" instead of "LoMem.h", but it will
  77.                          also make porting to another compiler easier.
  78.                     -    added THREAD_DEBUG so thread debug code can be selectively
  79.                          disabled without defining NDEBUG and surrounded debug
  80.                          functions with conditional compile directives to reduce
  81.                          dead-code size in non-debug version
  82.                     -    uses the OS routines Enqueue and Dequeue instead of my
  83.                          own linked-list code. this made the code cleaner and will
  84.                          make the object code even more compact.
  85.                     -    made defines with prefix "LM" for all low-memory globals
  86.     940217 aih    -    put back THREAD_SET_GLOBALS code in attempt to fix
  87.                         update problems
  88.     940217 aih    -    added string to assertion debug statement, and added ifdef
  89.                          around assertfailed function to keep it from being compiled
  90.                          into non-debug version
  91.     940217 aih    -    release 1.0d2
  92.                     -    for efficiency, defined TickCount as Ticks low-memory global
  93.                     -    for greater ease in adding ThreadLib to other people's
  94.                         applications, removed use of exceptions. this also increases
  95.                         the efficiency of context switches in applications that
  96.                         don't use exceptions
  97.     940215 aih    -    removed THREAD_SET_GLOBALS code since it didn't seem to be
  98.                         needed
  99.     940214 aih    -    added thread serial numbers
  100.                     -    added precondition for ThreadEnd
  101.                     -    fixed problem with defer and combine stack adjusts
  102.     940213 aih    -    added stack sniffer VBL task
  103.                     -    added functions for determining the minimum and the default
  104.                          stack sizes for threads and removed THREAD_STACK_SIZE
  105.     940212 aih    -    made threads use pointers instead of handles. using pointers
  106.                         increases the efficiency of Thread Library. this won't
  107.                         significantly increase memory fragmentation since stacks
  108.                         for threads are already allocated as pointers. (while no
  109.                         stack is allocated for the main thread, the main thread
  110.                         is created very early in the application and usually
  111.                         remains in existence until the application exits.)
  112.                     -    limited release 1.0d1.1
  113.     940211 aih    -    added thread status field
  114.     940210 aih    -    release 1.0d1; got threads to work without crashing! yay! :-)
  115.     940204 aih    -    created */
  116.  
  117. /*----------------------------------------------------------------------------*/
  118. /* include statements */
  119. /*----------------------------------------------------------------------------*/
  120.  
  121. #include <Errors.h>
  122. #include <Events.h>
  123. #include <Gestalt.h>
  124. #include <LowMem.h>
  125. #include <Memory.h>
  126. #include <OSUtils.h>
  127. #include <Windows.h>
  128. #include "ThreadLibrary.h"
  129.  
  130. /*----------------------------------------------------------------------------*/
  131. /* compiler-specific code */
  132. /*----------------------------------------------------------------------------*/
  133.  
  134. #if ! defined(THINK_C) && ! defined(__MWERKS__) && ! defined(MPW)
  135.  
  136.     /* For compilers other than THINK C, you should be careful when turning
  137.         on the optimizer. For instance, I found that the "defer and combine
  138.         stack adjusts" option was incompatible with Thread Library. I
  139.         recommend first getting Thread Library to work with all
  140.         optimizations disabled. Once you know that it runs under the new
  141.         compiler, you can enable the optimizations. If it then crashes,
  142.         you will know that one or more of the optimizations performed by
  143.         the compiler are incompatible with Thread Library and should
  144.         therefore be disabled. */
  145.  
  146.     #error tested with THINK C 7.0, MetroWerks CW4, MPW 3.3.1
  147.  
  148. #endif
  149.  
  150. #ifdef THINK_C
  151.  
  152.     /* Thread Library will not work correctly (the heap will become
  153.         corrupted) when the THINK C "defer and combine stack adjusts"
  154.         optimization option is enabled. We therefore disable the option
  155.         for this file. All other THINK C optimizations work fine with
  156.         Thread Library. */
  157.     #pragma options(! defer_adjust)
  158.     
  159.     /*    Thread Library will not work if profiling is enabled. During
  160.         context switches, the profiler's state is inconsistent, since the
  161.         profiler's internal state may refer to the prior executing thread
  162.         while the new thread is being switched in. This could result in a
  163.         nasty crash. */
  164.     #pragma options(! profile)
  165.     
  166. #endif /* THINK_C */
  167.  
  168. #ifdef __MWERKS__
  169.  
  170.     /*    See note for THINK C profiler. */
  171.     #pragma profiler off
  172.  
  173. #endif /* __MWERKS__ */
  174.  
  175. /*----------------------------------------------------------------------------*/
  176. /* debug declarations */
  177. /*----------------------------------------------------------------------------*/
  178.  
  179. /* Define THREAD_DEBUG as 1 to enable debug code, or 0 to disable debug code.
  180.     You can also define NDEBUG to disable debug code. Debug code is enabled
  181.     by default if both THREAD_DEBUG and NDEBUG are undefined. */
  182. #ifndef THREAD_DEBUG
  183.     #ifndef NDEBUG
  184.         #define THREAD_DEBUG            (1)
  185.     #else
  186.         #define THREAD_DEBUG            (0)
  187.     #endif
  188. #endif /* THREAD_DEBUG */
  189.  
  190. #if THREAD_DEBUG    
  191.  
  192.     #define ThreadAssert(x)    ((void) ((x) || ThreadAssertFailed(#x)))    
  193.  
  194.     static int ThreadAssertFailed(const char *msg)
  195.     {
  196.         const char *prompt = "ThreadAssertFailed:";
  197.         Str255 pmsg;
  198.         short i, j;
  199.         
  200.         for (i = 0; i < 255 && prompt[i]; i++)
  201.             pmsg[i+1] = prompt[i];
  202.         for (j = 0; i+j < 255 && msg[j]; j++)
  203.             pmsg[i+j+1] = msg[j];
  204.         pmsg[0] = i+j;
  205.         DebugStr(pmsg);
  206.         return(0);
  207.     }
  208.  
  209. #else /* THREAD_DEBUG */
  210.  
  211.     #define ThreadAssert(x)    ((void) 0)
  212.  
  213. #endif /* THREAD_DEBUG */
  214.  
  215. #define require(x)    ThreadAssert(x)
  216. #define check(x)        ThreadAssert(x)
  217. #define ensure(x)        ThreadAssert(x)
  218.  
  219. /*----------------------------------------------------------------------------*/
  220. /* low-memory globals */
  221. /*----------------------------------------------------------------------------*/
  222.  
  223. /*    Two low-memory globals are not defined in "LowMem.h".
  224.  
  225.     The low-memory global variable HiHeapMark must be set when threads
  226.     are switched so that QuickDraw will operate correctly. This may
  227.     no longer be necessary with the Modern Memory Manager, or with
  228.     the PowerPC version of QuickDraw. At least, that's what I'm hoping
  229.     is the case. Otherwise, we may have to "break" the rules a bit
  230.     to munge HiHeapMark into what we need.
  231.     
  232.     The low-memory global variable StkLowPt must be cleared when any
  233.     thread other than the main thread is switched in. This is necessary
  234.     to disable the system's stack sniffer VBL task, which otherwise
  235.     would cause system error #28. */
  236.         
  237. #define HiHeapMark                    (0x0BAE)
  238. #define StkLowPt                        (0x0110)
  239. #define LMGetHiHeapMark()            (*(Ptr *) HiHeapMark)
  240. #define LMSetHiHeapMark(p)            ((void) (*(Ptr *) HiHeapMark = (p)))
  241. #define LMGetStkLowPt(p)            (*(Ptr *) StkLowPt)
  242. #define LMSetStkLowPt(p)            ((void) (*(Ptr *) StkLowPt = (p)))
  243.  
  244. /*----------------------------------------------------------------------------*/
  245. /* register access */
  246. /*----------------------------------------------------------------------------*/
  247.  
  248. #ifdef powerc
  249.  
  250.     #include "regppc.h"
  251.  
  252.     /* indexes to special registers */
  253.     #define REGISTER_FP    (1)
  254.     #define REGISTER_SP    (3)
  255.  
  256.     typedef struct { ThreadRegistersPPCType gp; } ThreadRegistersType;
  257.     
  258.     /* save the registers */
  259.     #define ThreadRegistersFPSave(registers) ((void) 0)
  260.     #define ThreadRegistersGPSave(registers) ThreadRegistersPPCSave(registers)
  261.     
  262.     /* restore the registers */
  263.     #define ThreadRegistersFPRestore(registers) ((void) 0)
  264.     #define ThreadRegistersGPRestore(registers) ThreadRegistersPPCRestore((registers), 1)
  265.  
  266. #else /* powerc */
  267.     
  268.     /* indexes to special registers */
  269.     #define REGISTER_FP    (10)
  270.     #define REGISTER_SP    (11)
  271.  
  272.     /* arrays of registers */
  273.     typedef long ThreadRegistersGPType[12];
  274.     typedef long ThreadRegistersFPType[12];
  275.     
  276.     /* all of the registers saved by threads */
  277.     typedef struct {
  278.         ThreadRegistersGPType gp;            /* general purpose registers */
  279.         ThreadRegistersFPType fp;            /* floating point registers */
  280.     } ThreadRegistersType;
  281.  
  282.     /* save the general purpose registers (similar to setjmp) */
  283.     #pragma parameter __D0 ThreadRegistersGPSave(__A0)
  284.     static long ThreadRegistersGPSave(ThreadRegistersGPType registers) =
  285.     {
  286.         /*
  287.             asm {
  288.                     lea        @1, a1
  289.                     moveq        #0, d0
  290.                     movem.l    d3-d7/a1-a7, (a0)
  291.             @1
  292.             }
  293.         */
  294.         0x43FA, 0x0008,
  295.         0x7000,
  296.         0x48D0, 0xFEF8
  297.     };
  298.  
  299.     /* restore the general purpose registers and resume execution of the
  300.         saved thread (similar to longjmp) */
  301.     #pragma parameter ThreadRegistersGPRestore(__A0)
  302.     static void ThreadRegistersGPRestore(ThreadRegistersGPType registers) =
  303.     {
  304.         /*
  305.             asm {
  306.                 moveq        #1, d0
  307.                 movem.l    (a0), d3-d7/a1-a7
  308.                 jmp        (a1)
  309.             }
  310.         */
  311.         0x7001,
  312.         0x4CD0, 0xFEF8,
  313.         0x4ED1
  314.     };
  315.  
  316.     /* save the floating point registers */
  317.     #pragma parameter ThreadRegistersFPSave(__A0)
  318.     static void ThreadRegistersFPSave(ThreadRegistersFPType registers) =
  319.     {
  320.         /*
  321.             asm {
  322.                     fmovem.l    fp4-fp7, (a0)
  323.             }
  324.         */
  325.         0xF210, 0xF00F
  326.     };
  327.  
  328.     /* restore the floating point registers */
  329.     #pragma parameter ThreadRegistersFPRestore(__A0)
  330.     static void ThreadRegistersFPRestore(ThreadRegistersFPType registers) =
  331.     {
  332.         /* asm { fmovem.l    (a0), fp4-fp7 } */
  333.         0xF210, 0xD00F
  334.     };
  335.  
  336. #endif /* powerc */
  337.  
  338. /* return the value of the stack pointer */
  339. static ThreadStackPtr ThreadRegisterStackPointer(void)
  340. {
  341.     ThreadRegistersType registers;
  342.  
  343.     (void) ThreadRegistersGPSave(registers.gp);
  344.     return((ThreadStackPtr) registers.gp[REGISTER_SP]);
  345. }
  346.  
  347. /* return the value of the stack frame pointer or link register */
  348. static ThreadStackPtr ThreadRegisterStackFramePointer(void)
  349. {
  350.     ThreadRegistersType registers;
  351.  
  352.     (void) ThreadRegistersGPSave(registers.gp);
  353.     return((ThreadStackPtr) registers.gp[REGISTER_FP]);
  354. }
  355.  
  356. /*----------------------------------------------------------------------------*/
  357. /* type definitions, global variables, etc. */
  358. /*----------------------------------------------------------------------------*/
  359.  
  360. /* structure describing a thread */
  361. typedef struct ThreadStructure {
  362.     
  363.     /* queue links */
  364.     struct ThreadStructure *next;        /* next thread in queue */
  365.     struct ThreadStructure *prev;        /* previous thread in queue */
  366.     
  367.     /* internal state */
  368.     ThreadRegistersType registers;    /* saved registers */
  369.     ThreadErrorType error;                /* error code from last function called */
  370.     ThreadTicksType wake;                /* when to wake thread */
  371.     ThreadType sn;                            /* thread's serial number */
  372.     Boolean enabled;                        /* true if thread is enabled */
  373.     Boolean fpu;                            /* true if saves floating point state */
  374.     Boolean main;                            /* true if the main thread */
  375.     
  376.     /* thread's stack */
  377.     struct {
  378.         ThreadStackPtr top;                /* highest address in thread's stack */
  379.         ThreadStackPtr limit;            /* lowest address in thread's stack */
  380.         ThreadSizeType size;                /* size of thread's stack */
  381.     } stack;
  382.  
  383.     /* low-memory globals */
  384.     struct {
  385.         Ptr heapEnd;                        /* value of HeapEnd low-memory global */
  386.         Ptr applLimit;                        /* value of ApplLimit low-memory global */
  387.         Ptr hiHeapMark;                    /* value of HiHeapMark low-memory global */
  388.     } lm;
  389.     
  390.     /* application defined functions and data */
  391.     struct {
  392.         ThreadProcBeginEndType begin;    /* called when thread is started */
  393.         ThreadProcBeginEndType end;    /* called when thread is terminated */
  394.         ThreadProcType resume;            /* called when thread is resumed */
  395.         ThreadProcType suspend;            /* called when thread is suspended */
  396.         ThreadProcType entry;            /* thread's entry point */
  397.         void *data;                            /* data to pass to thread callbacks */
  398.     } appl;
  399.     
  400. } ThreadStructure, *ThreadPtr;
  401.  
  402. /* queue of threads */
  403. typedef struct {
  404.     ThreadPtr head;                        /* head of queue */
  405.     ThreadPtr tail;                        /* tail of queue */
  406.     long nelem;                                /* number of elements in queue */
  407. } ThreadQueueStructure, *ThreadQueuePtr;
  408.  
  409. /* structure describing state of thread library */
  410. typedef struct {
  411.     ThreadQueueStructure queue;        /* queue of threads */
  412.     ThreadErrorType error;                /* error code from last function called */
  413.     ThreadType lastsn;                    /* serial number of last thread created */
  414.     ThreadPtr main;                        /* main thread */
  415.     ThreadPtr active;                        /* currently active thread */
  416.     ThreadPtr zombie;                        /* thread to dispose of */
  417.     ThreadProcScheduleType schedule;    /* scheduler callback */
  418.     ThreadProcAllocateType allocate;    /* memory allocation callback */
  419.     ThreadProcDisposeType dispose;    /* memory disposal callback */
  420. } ThreadStateStructure;
  421.  
  422. /* state of thread library */
  423. static ThreadStateStructure gThread;
  424.  
  425. /* a few forward-declarations of functions */
  426. static ThreadPtr ThreadFromSN(register ThreadType tsn);
  427. static void ThreadDispose(ThreadPtr thread);
  428.  
  429. /*----------------------------------------------------------------------------*/
  430. /*    more register access code */
  431. /*----------------------------------------------------------------------------*/
  432.  
  433. /* return thread's stack pointer */
  434. static ThreadStackPtr ThreadStackPointer(ThreadPtr thread)
  435. {
  436.     ThreadStackPtr sp = NULL;
  437.     
  438.     if (thread == gThread.active)
  439.         sp = ThreadRegisterStackPointer();
  440.     else
  441.         sp = (ThreadStackPtr) thread->registers.gp[REGISTER_SP];
  442.     return(sp);
  443. }
  444.  
  445. /* return thread's register stack frame pointer */
  446. ThreadStackPtr ThreadStackFramePointer(ThreadType tsn)
  447. {
  448.     ThreadStackPtr fp = NULL;
  449.     ThreadPtr thread;
  450.     
  451.     thread = ThreadFromSN(tsn);
  452.     if (thread == gThread.active)
  453.         fp = ThreadRegisterStackFramePointer();
  454.     else
  455.         fp = (ThreadStackPtr) thread->registers.gp[REGISTER_FP];
  456.     return(fp);
  457. }
  458.  
  459. /*----------------------------------------------------------------------------*/
  460. /* memory allocation */
  461. /*----------------------------------------------------------------------------*/
  462.  
  463. /*    This header is inserted at the start of every block of memory allocated
  464.     by Thread Library. Among other information, the header also contains a
  465.     pointer to the disposal function to use when disposing of the block of
  466.     memory. Storing a pointer to the disposal function with each block allows
  467.     the application to change the memory allocator or disposal functions after
  468.     the block is allocated, thus eliminating a possible source of errors. */
  469. typedef struct {
  470.     #if THREAD_DEBUG
  471.         ThreadTypeType type;            /* type of block */
  472.         ThreadSizeType size;            /* size of block */
  473.     #endif
  474.     ThreadProcDisposeType dispose;/* function to use to dispose of block */
  475. } ThreadMemoryHeaderType;
  476.  
  477. #if THREAD_DEBUG
  478.  
  479.     /* These functions are used while debugging to help validate memory
  480.         and to ensure that discarded memory is unuseable. */
  481.         
  482.     /*    Return the size of a pointer. */
  483.     static ThreadSizeType PtrSize(void *p)
  484.     {
  485.         return(((ThreadMemoryHeaderType *) p)[-1].size);
  486.     }
  487.     
  488.     /*    Return the type of a pointer. */
  489.     static ThreadSizeType PtrType(void *p)
  490.     {
  491.         return(((ThreadMemoryHeaderType *) p)[-1].type);
  492.     }
  493.  
  494.     /* Fill a pointer with garbage. */
  495.     static void *PtrJunk(void *p)
  496.     {
  497.         char *q;
  498.         ThreadSizeType i;
  499.         ThreadSizeType n;
  500.         
  501.         q = p;
  502.         n = PtrSize(p);
  503.         for (i = 0; i < n; i++)
  504.             q[i] = ~0;
  505.         return(p);
  506.     }
  507.  
  508. #else /* THREAD_DEBUG */
  509.  
  510.     #define PtrJunk(p)            ((void) 0)
  511.     
  512. #endif /* THREAD_DEBUG */
  513.  
  514. /*    Returns the amount of memory needed to store a block of the specified
  515.     size. This includes the overhead of the block header. */
  516. static ThreadSizeType PtrSizeNew(ThreadSizeType size, ThreadTypeType type)
  517. {
  518.     return(size + sizeof(ThreadMemoryHeaderType));
  519. }
  520.  
  521. /* Allocate n contiguous bytes using an application supplied allocator,
  522.     or the default allocator (NewPtr) if no allocator was supplied by
  523.     the application. The disposal function installed at the time the
  524.     memory is allocated is saved with the block, so that it may be
  525.     called to dispose of the block even the application subsequently
  526.     changes the memory allocator. */
  527. static void *PtrBegin(ThreadSizeType size, ThreadTypeType type)
  528. {
  529.     ThreadMemoryHeaderType *hdr;
  530.     
  531.     hdr = NULL;
  532.     if (gThread.allocate) {
  533.         hdr = gThread.allocate(PtrSizeNew(size, type), type);
  534.         if (hdr)
  535.             hdr->dispose = gThread.dispose;
  536.     }
  537.     if (! hdr && ! ThreadError()) {
  538.         hdr = (ThreadMemoryHeaderType *) NewPtr(PtrSizeNew(size, type));
  539.         if (hdr)
  540.             hdr->dispose = NULL;
  541.         else
  542.             ThreadErrorSet(MemError() ? MemError() : memFullErr);
  543.     }
  544.     #if THREAD_DEBUG
  545.         if (hdr) {
  546.             hdr->type = type;
  547.             hdr->size = size;
  548.         }
  549.     #endif
  550.     if (! hdr && ! ThreadError())
  551.         ThreadErrorSet(memFullErr);
  552.     return(hdr ? hdr + 1 : NULL);
  553. }
  554.  
  555. /* allocate and clear memory */
  556. static void *PtrBeginClear(ThreadSizeType size, ThreadTypeType type)
  557. {
  558.     ThreadSizeType i;
  559.     char *p;
  560.     
  561.     p = PtrBegin(size, type);
  562.     if (p) {
  563.         for (i = 0; i < size; i++)
  564.             p[i] = 0;
  565.     }
  566.     return(p);
  567. }
  568.  
  569. /* dispose of the memory */
  570. static void PtrEnd(void *p, ThreadSizeType size, ThreadTypeType type)
  571. {
  572.     ThreadMemoryHeaderType *hdr;
  573.  
  574.     if (p) {
  575.         PtrJunk(p);
  576.         hdr = p;
  577.         hdr--;
  578.         #if THREAD_DEBUG
  579.             check(hdr->size == size && hdr->type == type);
  580.             hdr->type = THREAD_TYPE_FREE;
  581.             hdr->size = -1;
  582.         #endif
  583.         if (hdr->dispose)
  584.             hdr->dispose(hdr, PtrSizeNew(size, type), type);
  585.         else
  586.             DisposePtr((Ptr) hdr);
  587.     }
  588. }
  589.         
  590. /*----------------------------------------------------------------------------*/
  591. /*    Everything up to this point was just definitions and utility functions.
  592.     This is where the real meat of the code begins. The code from here on
  593.     is much cleaner and contains few ugly preprocessor directives. */
  594. /*----------------------------------------------------------------------------*/
  595.  
  596. /*----------------------------------------------------------------------------*/
  597. /*    Thread Validation */
  598. /*----------------------------------------------------------------------------*/
  599.  
  600. #if THREAD_DEBUG
  601.  
  602. /*    ThreadValid returns true if the 'thread' parameter is a valid thread.
  603.     This function is primarily for use during debugging, and is called
  604.     by the preconditions to most of the functions in Thread Library. You
  605.     will usually not need to call this function. So that this function can
  606.     easily be called from within other thread functions, the error code is not
  607.     set by this function. */
  608. static Boolean ThreadValid(ThreadPtr thread)
  609. {
  610.     if (! thread || PtrSize(thread) != sizeof(ThreadStructure)) return(false);
  611.     if (PtrType(thread) != THREAD_TYPE_THREAD) return(false);
  612.     if (thread->sn <= 0 || gThread.lastsn < thread->sn) return(false);
  613.     if (gThread.main) {
  614.         if (thread->main) {
  615.             if (thread->appl.entry) return(false);
  616.         }
  617.         else {
  618.             if (! thread->appl.entry) return(false);
  619.             if (! thread->stack.limit) return(false);
  620.             if (PtrType(thread->stack.limit) != THREAD_TYPE_STACK) return(false);
  621.         }
  622.     }
  623.     return(true);
  624. }
  625.  
  626. /*    Validate all threads. */
  627. static Boolean ThreadValidAll(void)
  628. {
  629.     Boolean valid;
  630.     ThreadPtr thread;
  631.     ThreadPtr first;
  632.     
  633.     valid = true;
  634.     thread = first = gThread.queue.head;
  635.     if (thread) {
  636.         do {
  637.             valid = ThreadValid(thread);
  638.             thread = thread->next;
  639.         } while (thread != first && valid);
  640.     }
  641.     return(valid);
  642. }
  643.  
  644. #endif /* THREAD_DEBUG */
  645.  
  646. /*----------------------------------------------------------------------------*/
  647. /* Private Queue Operations */
  648. /*----------------------------------------------------------------------------*/
  649.  
  650. /*    Threads are kept in a circular queue of threads. A doubly-linked list is
  651.     used to make removal of an arbitrary thread (not just the head of the
  652.     queue) efficient. */
  653.     
  654. #if THREAD_DEBUG
  655.  
  656. /* ThreadQueueValid returns true if the queue is valid. */
  657. static Boolean ThreadQueueValid(ThreadQueuePtr queue)
  658. {
  659.     if (! queue) return(false);
  660.     if (queue->nelem < 0) return(false);
  661.     if (queue->nelem == 0 && (queue->head || queue->tail)) return(false);
  662.     if (queue->nelem > 0 && (! queue->head || ! queue->tail)) return(false);
  663.     if (queue->nelem == 1 && queue->head != queue->tail) return(false);
  664.     if (queue->nelem > 1 && queue->head == queue->tail) return(false);
  665.     if (queue->head && ! ThreadValid(queue->head)) return(false);
  666.     if (queue->tail && ! ThreadValid(queue->tail)) return(false);
  667.     return(true);
  668. }
  669.  
  670. #endif /* THREAD_DEBUG */
  671.  
  672. /* ThreadEnqueue adds the thread to the end of the queue. */
  673. static void ThreadEnqueue(ThreadQueuePtr queue, ThreadPtr thread)
  674. {
  675.     require(ThreadQueueValid(queue));
  676.     require(ThreadValid(thread));
  677.     require(! ThreadValid(thread->next));
  678.     require(! ThreadValid(thread->prev));
  679.     if (! queue->head) {
  680.         check(! queue->tail);
  681.         queue->head = queue->tail = thread;
  682.         thread->prev = thread->next = thread;
  683.     }
  684.     else {
  685.         check(queue->tail != NULL);
  686.         queue->tail->next = thread;
  687.         thread->prev = queue->tail;
  688.         thread->next = queue->head;
  689.         queue->head->prev = thread;
  690.         queue->tail = thread;
  691.     }
  692.     queue->nelem++;
  693.     ensure(queue->tail == thread);
  694.     ensure(ThreadValid(thread->next));
  695.     ensure(ThreadValid(thread->prev));
  696.     ensure(ThreadQueueValid(queue));
  697. }
  698.  
  699. /* ThreadDequeue removes the thread from the queue. */
  700. static void ThreadDequeue(ThreadQueuePtr queue, ThreadPtr thread)
  701. {
  702.     require(ThreadQueueValid(queue));
  703.     require(ThreadValid(thread));
  704.     require(ThreadValid(thread->next));
  705.     require(ThreadValid(thread->prev));
  706.     require(queue->nelem > 0);
  707.     if (thread == queue->head || thread == queue->tail) {
  708.         if (queue->nelem == 1)
  709.             queue->head = queue->tail = NULL;
  710.         else {
  711.             if (thread == queue->head)
  712.                 queue->head = thread->next;
  713.             else
  714.                 queue->tail = thread->prev;
  715.             queue->tail->next = queue->head;
  716.             queue->head->prev = queue->tail;
  717.         }
  718.     }
  719.     else {
  720.         check(thread->prev != thread && thread->next != thread);
  721.         check(queue->nelem > 0);
  722.         thread->prev->next = thread->next;
  723.         thread->next->prev = thread->prev;
  724.     }
  725.     thread->next = thread->prev = NULL;
  726.     queue->nelem--;
  727.     ensure(! ThreadValid(thread->next));
  728.     ensure(! ThreadValid(thread->prev));
  729.     ensure(ThreadQueueValid(queue));
  730. }
  731.  
  732. /*----------------------------------------------------------------------------*/
  733. /*    Error Handling */
  734. /*----------------------------------------------------------------------------*/
  735.  
  736. /*    returns the last error that occurred, or THREAD_ERROR_NONE
  737.     if the last routine completed successfully */
  738. ThreadErrorType ThreadError(void)
  739. {
  740.     return(gThread.active ? gThread.active->error : gThread.error);
  741. }
  742.  
  743. /*    sets the error code that will be returned by a
  744.     subsequent call to ThreadError */
  745. void ThreadErrorSet(ThreadErrorType error)
  746. {
  747.     gThread.error = error;
  748.     if (gThread.active)
  749.         gThread.active->error = error;
  750.     ensure(ThreadError() == error);
  751. }
  752.  
  753. /*----------------------------------------------------------------------------*/
  754. /*    Thread Serial Numbers */
  755. /*----------------------------------------------------------------------------*/
  756.  
  757. /*    Every thread is assigned a unique serial number. Serial numbers are used
  758.     to refer to threads, rather than using a pointer, since there is always
  759.     the possiblity that a thread may have terminated before a thread pointer
  760.     is used, which would make a thread pointer invalid. The specific
  761.     assignment of serial numbers to threads is not defined by the interface,
  762.     though every valid thread is guaranteed a non-zero serial number.
  763.     
  764.     Note: you should not assume that any thread will have a specific
  765.     serial number. */
  766.  
  767. /* returns the thread's serial number; the error code is not changed. */
  768. static ThreadType ThreadSN(ThreadPtr thread)
  769. {
  770.     require(! thread || ThreadValid(thread));
  771.     return(thread ? thread->sn : THREAD_NONE);
  772. }
  773.  
  774. /*    Given the serial number of a thread, ThreadFromSN returns the corresponding
  775.     thread pointer, or NULL if there is no thread with the specified serial
  776.     number. If the thread is found, the error code is cleared, otherwise it
  777.     is set to THREAD_ERROR_NOT_FOUND. Since the error code is always either
  778.     set or cleared, it is unnecessary to set the error code in many simple
  779.     accessor functions that use ThreadFromSN. */
  780. static ThreadPtr ThreadFromSN(register ThreadType tsn)
  781. {
  782.     register ThreadPtr thread;
  783.     register long nthread;
  784.         
  785.     require(0 <= tsn && tsn <= gThread.lastsn);
  786.     thread = gThread.queue.head;
  787.     nthread = gThread.queue.nelem;
  788.     while (nthread-- > 0 && thread->sn != tsn)
  789.         thread = thread->next;
  790.     if (thread && thread->sn != tsn)
  791.         thread = NULL;
  792.     ThreadErrorSet(thread ? THREAD_ERROR_NONE : THREAD_ERROR_NOT_FOUND);
  793.     ensure(! thread || (ThreadValid(thread) && thread->sn == tsn));
  794.     return(thread);
  795. }
  796.  
  797. /*----------------------------------------------------------------------------*/
  798. /*    Accessing the Queue of Threads */
  799. /*----------------------------------------------------------------------------*/
  800.  
  801. /* returns the number of threads in the queue */
  802. long ThreadCount(void)
  803. {
  804.     ThreadErrorSet(THREAD_ERROR_NONE);
  805.     return(gThread.queue.nelem);
  806. }
  807.  
  808. /* returns the main thread, or THREAD_NONE if there are no
  809.     threads */
  810. ThreadType ThreadMain(void)
  811. {
  812.     ThreadErrorSet(THREAD_ERROR_NONE);
  813.     return(ThreadSN(gThread.main));
  814. }
  815.  
  816. /* returns the currently active thread, or THREAD_NONE if
  817.     there are no threads */
  818. ThreadType ThreadActive(void)
  819. {
  820.     ThreadErrorSet(THREAD_ERROR_NONE);
  821.     return(ThreadSN(gThread.active));
  822. }
  823.  
  824. /* returns the first thread in the queue of threads, or
  825.     THREAD_NONE if there are no threads */
  826. ThreadType ThreadFirst(void)
  827. {
  828.     ThreadErrorSet(THREAD_ERROR_NONE);
  829.     return(ThreadSN(gThread.queue.head));
  830. }
  831.  
  832. /* returns the next thread in the circular queue of threads,
  833.     or THREAD_NONE if there are no threads */
  834. ThreadType ThreadNext(ThreadType tsn)
  835. {
  836.     ThreadPtr thread;
  837.     
  838.     thread = ThreadFromSN(tsn);
  839.     return(thread ? ThreadSN(thread->next) : THREAD_NONE);
  840. }
  841.  
  842. /* returns true if the specified thread exists */
  843. Boolean ThreadExists(ThreadType tsn)
  844. {
  845.     ThreadPtr thread;
  846.     
  847.     thread = ThreadFromSN(tsn);
  848.     if (ThreadError() == THREAD_ERROR_NOT_FOUND)
  849.         ThreadErrorSet(THREAD_ERROR_NONE);
  850.     return(thread != NULL);
  851. }
  852.  
  853. /*----------------------------------------------------------------------------*/
  854. /*    Floating Point Unit */
  855. /*----------------------------------------------------------------------------*/
  856.  
  857. /*    true if there's a floating point unit */
  858. static Boolean ThreadHasFPU(void)
  859. {
  860.     long response;
  861.     Boolean result;
  862.     
  863.     result = false;
  864.     if (Gestalt(gestaltFPUType, &response) == noErr)
  865.         result = (response != gestaltNoFPU);
  866.     return(result);
  867. }
  868.  
  869. /*----------------------------------------------------------------------------*/
  870. /*    Application Defined Data */
  871. /*----------------------------------------------------------------------------*/
  872.  
  873. /* ThreadData returns the data field of the thread. The application can
  874.     use the thread's data field for its own purposes. */
  875. void *ThreadData(ThreadType tsn)
  876. {
  877.     ThreadPtr thread;
  878.     
  879.     thread = ThreadFromSN(tsn);
  880.     return(thread ? thread->appl.data : NULL);
  881. }
  882.  
  883. /* ThreadDataSet sets the data field of the thread. The application can
  884.     use the thread's data field for its own purposes. */
  885. void ThreadDataSet(ThreadType tsn, void *data)
  886. {
  887.     ThreadPtr thread;
  888.     
  889.     thread = ThreadFromSN(tsn);
  890.     if (thread)
  891.         thread->appl.data = data;
  892.     ensure(! thread || ThreadData(tsn) == data);
  893. }
  894.  
  895. /*----------------------------------------------------------------------------*/
  896. /*    Application Defined Scheduler Function */
  897. /*----------------------------------------------------------------------------*/
  898.  
  899. /*    returns the function used to schedule threads
  900.     or NULL if the default function is being used */
  901. ThreadProcScheduleType ThreadProcSchedule(void)
  902. {
  903.     ThreadErrorSet(THREAD_ERROR_NONE);
  904.     return(gThread.schedule);
  905. }
  906.  
  907. /* sets the function used to schedule threads; if you
  908.     specify NULL, then the default function will be used. */
  909. void ThreadProcScheduleSet(ThreadProcScheduleType schedule)
  910. {
  911.     ThreadErrorSet(THREAD_ERROR_NONE);
  912.     gThread.schedule = schedule;
  913.     ensure(ThreadProcSchedule() == schedule);
  914. }
  915.  
  916. /*----------------------------------------------------------------------------*/
  917. /*    Application Defined Memory Allocation Functions */
  918. /*----------------------------------------------------------------------------*/
  919.  
  920. /*    returns the function used to allocate memory
  921.     or NULL if the default function is being used */
  922. ThreadProcAllocateType ThreadProcAllocate(void)
  923. {
  924.     ThreadErrorSet(THREAD_ERROR_NONE);
  925.     return(gThread.allocate);
  926. }
  927.  
  928. /* sets the function used to allocate memory; if you
  929.     specify NULL, then the default function will be used. */
  930. void ThreadProcAllocateSet(ThreadProcAllocateType allocate)
  931. {
  932.     ThreadErrorSet(THREAD_ERROR_NONE);
  933.     gThread.allocate = allocate;
  934.     ensure(ThreadProcAllocate() == allocate);
  935. }
  936.  
  937. /*    returns the function used to dispose of memory,
  938.     or NULL if the default function is being used */
  939. ThreadProcDisposeType ThreadProcDispose(void)
  940. {
  941.     ThreadErrorSet(THREAD_ERROR_NONE);
  942.     return(gThread.dispose);
  943. }
  944.  
  945. /*    sets the function used to dispose of memory; if you
  946.     specify NULL, then the default function will be used. */
  947. void ThreadProcDisposeSet(ThreadProcDisposeType dispose)
  948. {
  949.     gThread.dispose = dispose;
  950.     ThreadErrorSet(THREAD_ERROR_NONE);
  951.     ensure(ThreadProcDispose() == dispose);
  952. }
  953.  
  954. /*----------------------------------------------------------------------------*/
  955. /*    Application Defined Protocol Functions */
  956. /*----------------------------------------------------------------------------*/
  957.  
  958. /* returns the begin function for the thread */
  959. ThreadProcBeginEndType ThreadProcBegin(ThreadType tsn)
  960. {
  961.     ThreadPtr thread;
  962.     
  963.     require(tsn != ThreadMain());
  964.     thread = ThreadFromSN(tsn);
  965.     return(thread ? thread->appl.begin : NULL);
  966. }
  967.  
  968. /* sets the begin function for the thread */
  969. void ThreadProcBeginSet(ThreadType tsn, ThreadProcBeginEndType begin)
  970. {
  971.     ThreadPtr thread;
  972.     
  973.     require(tsn != ThreadMain());
  974.     thread = ThreadFromSN(tsn);
  975.     if (thread)
  976.         thread->appl.begin = begin;
  977.     ensure(! thread || ThreadProcBegin(tsn) == begin);
  978. }
  979.  
  980. /* returns the end function for the thread */
  981. ThreadProcBeginEndType ThreadProcEnd(ThreadType tsn)
  982. {
  983.     ThreadPtr thread;
  984.     
  985.     require(tsn != ThreadMain());
  986.     thread = ThreadFromSN(tsn);
  987.     return(thread ? thread->appl.end : NULL);
  988. }
  989.  
  990. /* sets the end function for the thread */
  991. void ThreadProcEndSet(ThreadType tsn, ThreadProcBeginEndType end)
  992. {
  993.     ThreadPtr thread;
  994.     
  995.     require(tsn != ThreadMain());
  996.     thread = ThreadFromSN(tsn);
  997.     if (thread)
  998.         thread->appl.end = end;
  999.     ensure(! thread || ThreadProcEnd(tsn) == end);
  1000. }
  1001.  
  1002. /* returns the resume function for the thread */
  1003. ThreadProcType ThreadProcResume(ThreadType tsn)
  1004. {
  1005.     ThreadPtr thread;
  1006.     
  1007.     thread = ThreadFromSN(tsn);
  1008.     return(thread ? thread->appl.resume : NULL);
  1009. }
  1010.  
  1011. /* sets the resume function for the thread */
  1012. void ThreadProcResumeSet(ThreadType tsn, ThreadProcType resume)
  1013. {
  1014.     ThreadPtr thread;
  1015.     
  1016.     thread = ThreadFromSN(tsn);
  1017.     if (thread)
  1018.         thread->appl.resume = resume;
  1019.     ensure(! thread || ThreadProcResume(tsn) == resume);
  1020. }
  1021.  
  1022. /* returns the suspend function for the thread */
  1023. ThreadProcType ThreadProcSuspend(ThreadType tsn)
  1024. {
  1025.     ThreadPtr thread;
  1026.     
  1027.     thread = ThreadFromSN(tsn);
  1028.     return(thread ? thread->appl.suspend : NULL);
  1029. }
  1030.  
  1031. /* sets the suspend function for the thread */
  1032. void ThreadProcSuspendSet(ThreadType tsn, ThreadProcType suspend)
  1033. {
  1034.     ThreadPtr thread;
  1035.     
  1036.     thread = ThreadFromSN(tsn);
  1037.     if (thread)
  1038.         thread->appl.suspend = suspend;
  1039.     ensure(! thread || ThreadProcSuspend(tsn) == suspend);
  1040. }
  1041.  
  1042. /* returns the entry function for the thread */
  1043. ThreadProcType ThreadProcEntry(ThreadType tsn)
  1044. {
  1045.     ThreadPtr thread;
  1046.     
  1047.     thread = ThreadFromSN(tsn);
  1048.     return(thread ? thread->appl.entry : NULL);
  1049. }
  1050.  
  1051. /* sets the entry function for the thread */
  1052. void ThreadProcEntrySet(ThreadType tsn, ThreadProcType entry)
  1053. {
  1054.     ThreadPtr thread;
  1055.     
  1056.     thread = ThreadFromSN(tsn);
  1057.     if (thread)
  1058.         thread->appl.entry = entry;
  1059.     ensure(! thread || ThreadProcEntry(tsn) == entry);
  1060. }
  1061.  
  1062. /*----------------------------------------------------------------------------*/
  1063. /*    Information About the Stack */
  1064. /*----------------------------------------------------------------------------*/
  1065.  
  1066. /*    returns the recommended minimum stack size for a thread */
  1067. ThreadSizeType ThreadStackMinimum(void)
  1068. {
  1069.     ThreadErrorSet(THREAD_ERROR_NONE);
  1070.     return(LMGetMinStack());
  1071. }
  1072.  
  1073. /*    returns the default stack size for a thread */
  1074. ThreadSizeType ThreadStackDefault(void)
  1075. {
  1076.     ThreadErrorSet(THREAD_ERROR_NONE);
  1077.     return(LMGetDefltStack());
  1078. }
  1079.  
  1080. /* returns the total size of the thread's stack */
  1081. ThreadSizeType ThreadStackSize(ThreadType tsn)
  1082. {
  1083.     ThreadPtr thread;
  1084.     
  1085.     thread = ThreadFromSN(tsn);
  1086.     return(thread ? thread->stack.size : 0);
  1087. }
  1088.  
  1089. /*    returns the amount of space remaining in the thread's stack */
  1090. ThreadSizeType ThreadStackSpace(ThreadType tsn)
  1091. {
  1092.     ThreadSizeType result;
  1093.     ThreadStackPtr sp;
  1094.     ThreadPtr thread;
  1095.     
  1096.     result = 0;
  1097.     thread = ThreadFromSN(tsn);
  1098.     if (thread) {
  1099.         sp = ThreadStackPointer(thread);
  1100.         check(sp >= thread->stack.limit);
  1101.         result = sp - thread->stack.limit;
  1102.     }
  1103.     ensure(result >= 0);
  1104.     return(result);
  1105. }
  1106.  
  1107. /*----------------------------------------------------------------------------*/
  1108. /*    Scheduling */
  1109. /*----------------------------------------------------------------------------*/
  1110.  
  1111. /*    The three functions ThreadSchedule, ThreadActivate, and ThreadYield
  1112.     handle the scheduling and context switching of threads. These functions
  1113.     will be executed the most often of any of the functions in this file, and
  1114.     therefore will have the greatest impact on the efficiency of Thread
  1115.     Library. If you find Thread Library's context switches too slow, you
  1116.     could improve the efficiency of these functions. */
  1117.  
  1118. /*    Returns true if the thread is enabled. An enabled thread is eligible for
  1119.     scheduling, while a disabled thread is not scheduled.  */
  1120. Boolean ThreadEnabled(ThreadType tsn)
  1121. {
  1122.     ThreadPtr thread;
  1123.     
  1124.     thread = ThreadFromSN(tsn);
  1125.     return(thread ? thread->enabled : false);
  1126. }
  1127.  
  1128. /*    enables or disables the thread */
  1129. void ThreadEnabledSet(ThreadType tsn, Boolean enabled)
  1130. {
  1131.     ThreadPtr thread;
  1132.     
  1133.     require(tsn != ThreadMain());
  1134.     require(enabled == true || enabled == false);
  1135.     thread = ThreadFromSN(tsn);
  1136.     if (thread)
  1137.         thread->enabled = enabled;
  1138.     ensure(thread ?    ThreadEnabled(tsn) == enabled :
  1139.                             ThreadEnabled(tsn) == false);
  1140. }
  1141.  
  1142. /*    returns the time at which the thread will become active */
  1143. ThreadTicksType ThreadWakeTime(ThreadType tsn)
  1144. {
  1145.     ThreadPtr thread;
  1146.     
  1147.     thread = ThreadFromSN(tsn);
  1148.     return(thread ? thread->wake : 0);
  1149. }
  1150.  
  1151. /*    sets the time at which the thread will become active */
  1152. void ThreadWakeTimeSet(ThreadType tsn, ThreadTicksType wake)
  1153. {
  1154.     ThreadPtr thread;
  1155.     
  1156.     require(0 <= wake && wake <= THREAD_TICKS_MAX);
  1157.     thread = ThreadFromSN(tsn);
  1158.     if (thread)
  1159.         thread->wake = wake;
  1160.     ensure(ThreadWakeTime(tsn) == wake);
  1161. }
  1162.  
  1163. /* this is identical to ThreadSleepIntervalSet, but for greater efficiency it
  1164.     takes a pointer to a thread. */
  1165. static void ThreadSleepIntervalSetPtr(ThreadPtr thread, ThreadTicksType sleep, ThreadTicksType ticks)
  1166. {
  1167.     require(ThreadValid(thread));
  1168.     require(0 <= sleep && sleep <= THREAD_TICKS_MAX);
  1169.     /* Set the thread's wakeup time, being careful with overflow since the
  1170.         sleep parameter could be THREAD_TICKS_MAX. */
  1171.     if (sleep > THREAD_TICKS_MAX - ticks)
  1172.         thread->wake = THREAD_TICKS_MAX;
  1173.     else
  1174.         thread->wake = ticks + sleep;
  1175. }
  1176.  
  1177. /* sets the amount of time that the specified thread will remain inactive */
  1178. void ThreadSleepIntervalSet(ThreadType tsn, ThreadTicksType sleep)
  1179. {
  1180.     ThreadPtr thread;
  1181.     
  1182.     thread = ThreadFromSN(tsn);
  1183.     if (thread)
  1184.         ThreadSleepIntervalSetPtr(thread, sleep, LMGetTicks());
  1185. }
  1186.     
  1187. /*    ThreadSleepSet is the same as ThreadSleepIntervalSet, but is included
  1188.     for compatability with prior versions of Thread Library. */
  1189. void ThreadSleepSet(ThreadType tsn, ThreadTicksType sleep)
  1190. {
  1191.     ThreadSleepIntervalSet(tsn, sleep);
  1192. }
  1193.  
  1194. /*    EventPending returns true if an event is pending. Most events are posted
  1195.     to the event queue, so we can determine if an event is pending simply
  1196.     by examining the head of the event queue. Update events are not posted
  1197.     to the event queue, so we need to call CheckUpdate to detect them. Since
  1198.     CheckUpdate needs to traverse the window list and could therefore be
  1199.     relatively slow, we make the interval between calls to CheckUpdate as
  1200.     large as possible without severely degrading response time. Once every
  1201.     1/4 second seems like a good interval to call CheckUpdate.
  1202.     
  1203.     Activate and deactivate events are also not posted to the event queue.
  1204.     Instead, the system just sets the low-memory globals CurActivate and
  1205.     CurDeactive. However, these globals are not cleared once the event is
  1206.     handled. Therefore, I haven't figured out a reliable method to test
  1207.     for activate and deactivate events, so those events are just ignored.
  1208.     
  1209.     Checking for events using this method, rather than using EventAvail, is
  1210.     preferrable for two reasons. First, and most importantly, EventAvail
  1211.     can let the application be switched out, which could result in some
  1212.     confusing information being displayed about the application's memory
  1213.     partition in the Finder's About window and possibly other
  1214.     incompatabilities. Second, EventAvail is a fairly slow trap, and we
  1215.     want context switches to be as fast as possible. */
  1216. static Boolean EventPending(ThreadTicksType ticks)
  1217. {
  1218.     #define UPDATE_PENDING_INTERVAL (15)
  1219.     #define EVENT_PENDING_INTERVAL (1)
  1220.     static ThreadTicksType nextUpdate;
  1221.     static ThreadTicksType nextEvent;
  1222.     EventRecord event;
  1223.     Boolean pending;
  1224.  
  1225.     pending = false;
  1226.     if (ticks >= nextEvent) {
  1227.         pending = (LMGetEventQueue()->qHead != NULL);
  1228.         nextEvent = ticks + EVENT_PENDING_INTERVAL;
  1229.         if (! pending && ticks >= nextUpdate) {
  1230.             pending = CheckUpdate(&event);
  1231.             nextUpdate = ticks + UPDATE_PENDING_INTERVAL;
  1232.         }
  1233.     }
  1234.     return(pending);
  1235. }
  1236.  
  1237. /* ThreadSchedulePtr is identical to ThreadSchedule, except it returns a
  1238.     pointer to a thread instead of a thread serial number. This makes context
  1239.     switches triggered via ThreadYield more efficient, since we already have
  1240.     direct access to the relevant thread pointers and so do not need to waste
  1241.     time converting to and from thread serial numbers. */
  1242. static ThreadPtr ThreadSchedulePtr(register ThreadTicksType ticks)
  1243. {
  1244.     register ThreadPtr active;            /* active thread */
  1245.     register ThreadPtr newthread;        /* thread to switch to */
  1246.     ThreadType suggestedSN;                /* serial number of thread suggested by application */
  1247.     ThreadPtr suggestedPtr;                /* pointer to thread suggested by application */
  1248.  
  1249.     require(ThreadValid(gThread.active));
  1250.     if (EventPending(ticks)) {
  1251.         /* an event is pending, so return main thread */
  1252.         newthread = gThread.main;
  1253.     }
  1254.     else {
  1255.         /* round-robbin search for a thread that needs to be woken up */
  1256.         active = gThread.active;
  1257.         newthread = active->next;
  1258.         check(ThreadValid(newthread));
  1259.         while (newthread != active &&
  1260.                  (newthread->wake > ticks ||
  1261.                   ! newthread->enabled))
  1262.         {
  1263.             newthread = newthread->next;
  1264.             check(ThreadValid(newthread));
  1265.         }
  1266.         if (newthread == active &&
  1267.              (newthread->wake > ticks ||
  1268.               ! newthread->enabled))
  1269.         {
  1270.             /* no thread needs to be woken up, so return main thread */
  1271.             newthread = gThread.main;
  1272.         }
  1273.     }
  1274.     if (gThread.schedule) {
  1275.         /* use application-defined callback */
  1276.         suggestedSN = gThread.schedule(newthread->sn);
  1277.         if (suggestedSN != THREAD_NONE && suggestedSN != newthread->sn) {
  1278.             suggestedPtr = ThreadFromSN(suggestedSN);
  1279.             if (suggestedPtr && suggestedPtr->enabled)
  1280.                 newthread = suggestedPtr;
  1281.         }
  1282.     }
  1283.     ensure(ThreadValid(newthread));
  1284.     ensure(newthread->enabled);
  1285.     return(newthread);
  1286. }
  1287.  
  1288. /*    returns the next thread to activate */
  1289. ThreadType ThreadSchedule(void)
  1290. {
  1291.     ThreadErrorSet(THREAD_ERROR_NONE);
  1292.     return(ThreadSN(ThreadSchedulePtr(LMGetTicks())));
  1293. }
  1294.  
  1295. /*    ThreadResume restores the context of the active thread. It is called
  1296.     before a thread resumes execution, but after the thread's stack has
  1297.     been restored. */
  1298. static void ThreadResume(register ThreadPtr thread)
  1299. {
  1300.     register ThreadQueuePtr queue; /* for faster access to queue */
  1301.     
  1302.     require(thread == gThread.active && ThreadValid(thread));
  1303.     
  1304.     /* for greater efficiency, put things into registers */
  1305.     queue = &gThread.queue;
  1306.     
  1307.     /* Set the low-memory globals for the thread. We bypass any
  1308.         traps or glue (like SetApplLimit) to keep the OS from
  1309.         preventing us from changing these globals and to keep
  1310.         context switches fast. */
  1311.     LMSetHeapEnd(thread->lm.heapEnd);
  1312.     LMSetApplLimit(thread->lm.applLimit);
  1313.     LMSetHiHeapMark(thread->lm.hiHeapMark);
  1314.  
  1315.     /* Move the thread to the tail of the queue so that it is rescheduled
  1316.         to run after all other threads (though scheduling also depends on
  1317.         wake times and priority). Functionally, the move-to-tail is always
  1318.         equivalent to a dequeue followed by an enqueue, but it's optimized
  1319.         for the most common case where the thread is already at the front
  1320.         of the queue. Since the queue is circular, we can just advance the
  1321.         head and tail pointers to achieve the same result. We do the
  1322.         optimization in-line since the function call overhead could be
  1323.         significant over many context switches. */
  1324.     if (thread == queue->head) {
  1325.         queue->head = thread->next;
  1326.         queue->tail = thread;
  1327.     }
  1328.     else if (thread != queue->tail) {
  1329.         ThreadDequeue(queue, thread);
  1330.         ThreadEnqueue(queue, thread);
  1331.     }
  1332.  
  1333.     /* call the application's resume function -- this must be called prior to
  1334.         disposing of a zombie thread so that the application can switch its
  1335.         context to the new thread before destroying the data in the old thread */
  1336.     if (thread->appl.resume)
  1337.         thread->appl.resume(thread->appl.data);
  1338.  
  1339.     /* dispose of the memory allocated for a previously terminated
  1340.         thread (see ThreadEndPtr) */
  1341.     if (gThread.zombie) {
  1342.         ThreadDispose(gThread.zombie);
  1343.         gThread.zombie = NULL;
  1344.     }
  1345. }
  1346.  
  1347. /*    ThreadSuspend saves the context of the active thread. It is called
  1348.     when a thread is deactivated, but before the next thread's stack
  1349.     or environment have been restored. */
  1350. static void ThreadSuspend(register ThreadPtr thread)
  1351. {    
  1352.     require(thread == gThread.active && ThreadValid(thread));
  1353.     
  1354.     /* save low-memory globals */
  1355.     thread->lm.heapEnd = LMGetHeapEnd();
  1356.     thread->lm.applLimit = LMGetApplLimit();
  1357.     thread->lm.hiHeapMark = LMGetHiHeapMark();
  1358.  
  1359.     /* call the application's suspend function */
  1360.     if (thread->appl.suspend)
  1361.         thread->appl.suspend(thread->appl.data);
  1362. }
  1363.  
  1364. /* ThreadActivatePtr is identical to ThreadActivate, except it takes a pointer
  1365.     to a thread instead of a thread serial number. This makes context switches
  1366.     triggered via ThreadYield more efficient, since we already have direct
  1367.     access to the relevant thread pointers and so don't need to waste time
  1368.     converting to and from thread serial numbers.
  1369.     
  1370.     The context switch is accomplished by saving the CPU context with
  1371.     ThreadRegistersSave and then calling ThreadRegistersRestore, which
  1372.     jumps to the environment saved with ThreadRegistersSave when the
  1373.     thread being activated was last suspended. We don't have to use any
  1374.     assembly language glue since ThreadRegistersSave saved the value of
  1375.     the stack pointer, which at the time of the call to ThreadRegistersSave
  1376.     pointed somewhere in the thread's stack. The ThreadRegistersRestore
  1377.     function will restore the value of the stack pointer and will jump
  1378.     to the statement from which to resume the thread. ThreadRegistersRestore
  1379.     also restores all other registers. */
  1380. static void ThreadActivatePtr(register ThreadPtr oldthread, register ThreadPtr newthread)
  1381. {
  1382.     require(! oldthread || (oldthread == gThread.active && ThreadValid(gThread.active)));
  1383.     require(ThreadValid(newthread));
  1384.     if (oldthread != newthread) {
  1385.  
  1386.         /* save thread's registers */
  1387.         if (oldthread && oldthread->fpu)
  1388.             ThreadRegistersFPSave(oldthread->registers.fp);
  1389.         if (oldthread && ThreadRegistersGPSave(oldthread->registers.gp)) {
  1390.     
  1391.             /* The thread is being activated, so restore the thread's context.
  1392.                 We need to get the thread from the global variable gThread.active,
  1393.                 since local variables are not yet defined. */
  1394.             ThreadResume(gThread.active);
  1395.         }
  1396.         else {
  1397.     
  1398.             /* suspend the current thread */
  1399.             if (oldthread)
  1400.                 ThreadSuspend(oldthread);
  1401.             
  1402.             /* Jump to the specified thread. This suspends the current thread
  1403.                 and returns at the ThreadRegistersGPSave statement above (unless this
  1404.                 is the first time the thread is being executed, in which case it
  1405.                 returns at the ThreadRegistersGPSave statement in ThreadBegin). The
  1406.                 contents of the stack will be correct as soon as
  1407.                 ThreadRegistersGPRestore has completed, but ThreadResume must be called
  1408.                 before the thread can start executing again. */
  1409.             gThread.active = newthread;
  1410.             if (newthread->fpu)
  1411.                 ThreadRegistersFPRestore(newthread->registers.fp);
  1412.             ThreadRegistersGPRestore(newthread->registers.gp);
  1413.             check(false); /* doesn't return */
  1414.         }
  1415.     }
  1416.     /* can't evaluate this postcondition--no local variables */
  1417.     /* ensure(gThread.active == thread); */
  1418. }
  1419.  
  1420. /*    deactivates the currently active thread and makes the specified thread the
  1421.     active thread */
  1422. void ThreadActivate(ThreadType tsn)
  1423. {
  1424.     ThreadPtr thread;
  1425.     
  1426.     require(ThreadValid(gThread.active));
  1427.     thread = ThreadFromSN(tsn);
  1428.     if (thread)
  1429.         ThreadActivatePtr(gThread.active, thread);
  1430.     /* ensure(! thread || ThreadActive() == tsn); */ /* can't evaluate this postcondition */
  1431. }
  1432.  
  1433. /*    activates the next scheduled thread */
  1434. void ThreadYield(ThreadTicksType sleep)
  1435. {
  1436.     ThreadTicksType ticks;
  1437.  
  1438.     require(ThreadValid(gThread.active));
  1439.     /* We set the active thread's wakeup time before we run the scheduler and
  1440.         before the active thread is suspended. We need to set the wakeup time
  1441.         so that the thread scheduler will work correctly. Also, the thread
  1442.         scheduler could take several ticks to complete, while we want to
  1443.         calculate the wakeup time to be as close as possible to the wakeup
  1444.         time specified by the sleep parameter */
  1445.     ticks = LMGetTicks();
  1446.     ThreadErrorSet(THREAD_ERROR_NONE);
  1447.     ThreadSleepIntervalSetPtr(gThread.active, sleep, ticks);
  1448.     ThreadActivatePtr(gThread.active, ThreadSchedulePtr(ticks));
  1449. }
  1450.  
  1451. /*    returns the maximum time till the next call to ThreadYield */
  1452. ThreadTicksType ThreadYieldInterval(void)
  1453. {
  1454.     ThreadPtr thread;                /* for iterating through queue of threads */
  1455.     ThreadPtr active;                /* currently active thread */
  1456.     ThreadTicksType ticks;        /* current tick count */
  1457.     ThreadTicksType interval;    /* interval till next call to ThreadYield */
  1458.     
  1459.     require(ThreadValid(gThread.active));
  1460.     ThreadErrorSet(THREAD_ERROR_NONE);
  1461.     ticks = LMGetTicks();
  1462.     active = gThread.active;
  1463.     thread = active->next;
  1464.     interval = THREAD_TICKS_MAX;
  1465.     check(ThreadValid(thread));
  1466.     while (thread != active && interval) {
  1467.         if (thread->enabled) {
  1468.             if (thread->wake <= ticks)
  1469.                 interval = 0;
  1470.             else if (thread->wake - ticks < interval)
  1471.                 interval = thread->wake - ticks;
  1472.         }
  1473.         thread = thread->next;
  1474.         check(ThreadValid(thread));
  1475.     }
  1476.     ensure(0 <= interval && interval <= THREAD_TICKS_MAX);
  1477.     return(interval);
  1478. }
  1479.  
  1480. /*----------------------------------------------------------------------------*/
  1481. /*    Thread Creation and Destruction */
  1482. /*----------------------------------------------------------------------------*/
  1483.  
  1484. /*----------------------------------------------------------------------------*/
  1485. /*    Destroying Threads */
  1486. /*----------------------------------------------------------------------------*/
  1487.  
  1488. /*    ThreadDispose disposes of the memory allocated for the thread. The thread
  1489.     must not be active when this is called. ThreadDispose can be called either
  1490.     to dispose of a partially created thread (whose creation failed), or to
  1491.     dispose of a thread that has terminated. */
  1492. static void ThreadDispose(ThreadPtr thread)
  1493. {
  1494.     require(! thread || thread != gThread.active);
  1495.     if (thread) {
  1496.         if (thread->stack.limit && ! thread->main)
  1497.             PtrEnd(thread->stack.limit, thread->stack.size, THREAD_TYPE_STACK);
  1498.         PtrEnd(thread, sizeof(ThreadStructure), THREAD_TYPE_THREAD);
  1499.     }
  1500. }
  1501.  
  1502. /* ThreadEndPtr is identical in function to ThreadEnd (see below), but it
  1503.     takes a pointer to a thread rather than a thread serial number. */
  1504. static void ThreadEndPtr(ThreadPtr thread)
  1505. {
  1506.     require(ThreadValid(thread));
  1507.     if (thread) {
  1508.     
  1509.         /* suspend the thread if it's the active thread */
  1510.         if (thread == gThread.active)
  1511.             ThreadSuspend(thread);
  1512.         
  1513.         /* call the application's end function */
  1514.         if (thread->appl.end)
  1515.             thread->appl.end(thread->sn, thread->appl.data);
  1516.         
  1517.         /* remove the thread from the queue so that it is no longer
  1518.             accessible and will not be scheduled for execution */
  1519.         ThreadDequeue(&gThread.queue, thread);
  1520.     
  1521.         if (thread == gThread.main) {
  1522.     
  1523.             /* We're disposing of the main thread, so this is a good time
  1524.                 to do any final cleanup of Thread Library. */
  1525.                 
  1526.             /* clear globals */
  1527.             check(gThread.active == thread);
  1528.             check(gThread.queue.nelem == 0);
  1529.             gThread.main = gThread.active = NULL;
  1530.         }    
  1531.         else if (thread == gThread.active) {
  1532.         
  1533.             /* We're disposing of the active thread, but we can't dispose of
  1534.                 the thread's stack since we're still using that stack. So
  1535.                 we delay disposal of the thread until the next thread is
  1536.                 activated; the thread will be disposed of in ThreadResume,
  1537.                 which is executed when the next scheduled thread is activated. */
  1538.             check(! gThread.zombie);
  1539.             gThread.zombie = thread;
  1540.     
  1541.             /* activate the main thread */
  1542.             ThreadActivatePtr(NULL, gThread.main);
  1543.             check(false); /* doesn't return */
  1544.         }
  1545.         else {
  1546.             /* dispose of the memory allocated for the thread */
  1547.             ThreadDispose(thread);
  1548.         }
  1549.     }
  1550. }
  1551.  
  1552. /*    ThreadEnd removes the thread from the queue and disposes of the memory
  1553.     allocated for the thread. If the thread is the active thread then the
  1554.     next scheduled thread is activated. All threads (other than the main
  1555.     thread) must be disposed of before the main thread can be disposed of. */
  1556. void ThreadEnd(ThreadType tsn)
  1557. {
  1558.     ThreadPtr thread;
  1559.     
  1560.     require(tsn == THREAD_NONE ||
  1561.         (ThreadCount() > 1 ? tsn != ThreadMain() : tsn == ThreadMain()));
  1562.     if (tsn != THREAD_NONE) {
  1563.         thread = ThreadFromSN(tsn);
  1564.         if (thread)
  1565.             ThreadEndPtr(thread);
  1566.     }
  1567.     /* ensure(! ThreadValid(ThreadFromSN(tsn))); */ /* executing this would set the error code */
  1568. }
  1569.  
  1570. /*    ThreadEndAnyNext returns the next thread to be disposed of. Any thread
  1571.     other than the main thread may be returned. */
  1572. static ThreadType ThreadEndAnyNext(void)
  1573. {
  1574.     ThreadType tsn;
  1575.     
  1576.     tsn = THREAD_NONE;
  1577.     if (ThreadCount() > 1) {
  1578.         tsn = ThreadFirst();
  1579.         if (tsn == ThreadMain()) {
  1580.             tsn = ThreadNext(tsn);
  1581.             check(tsn != ThreadFirst());
  1582.         }
  1583.         check(tsn != ThreadMain());
  1584.     }
  1585.     ensure(    (    ThreadCount() <= 1 && tsn == THREAD_NONE) ||
  1586.                 (    ThreadCount() > 1 &&
  1587.                     ThreadValid(ThreadFromSN(tsn)) &&
  1588.                     tsn != ThreadMain()));
  1589.     return(tsn);
  1590. }
  1591.  
  1592. /*    ThreadEndAny disposes of any one thread other than the main thread. */
  1593. static void ThreadEndAny(void)
  1594. {
  1595.     ThreadEnd(ThreadEndAnyNext());
  1596.     /* ensure((old ThreadCount()) == 0 || ThreadCount() == (old ThreadCount()) - 1); */
  1597. }
  1598.  
  1599. /* ThreadEndAllExceptMain disposes of all threads other than the main thread. */
  1600. static void ThreadEndAllExceptMain(void)
  1601. {
  1602.     while (ThreadCount() > 1) {
  1603.         ThreadEndAny();
  1604.         /* check(ThreadCount() == (old ThreadCount()) - 1); */
  1605.     }
  1606.     ensure(ThreadCount() <= 1);
  1607. }
  1608.  
  1609. /*    ThreadEndAll disposes of all threads, including the main thread.
  1610.     ThreadEndAll is useful when your application is terminating and
  1611.     you want to dispose of any threads that may still exist.
  1612.     ThreadEndAll can be called only from within the main thread.  */
  1613. void ThreadEndAll(void)
  1614. {
  1615.     require(ThreadActive() == ThreadMain());
  1616.     ThreadEndAllExceptMain();
  1617.     ThreadEnd(ThreadMain());
  1618.     ensure(ThreadCount() == 0);
  1619. }
  1620.  
  1621. /*----------------------------------------------------------------------------*/
  1622. /*    Creating Threads */
  1623. /*----------------------------------------------------------------------------*/
  1624.  
  1625. /*    ThreadBeginMain creates the main application thread and returns the main
  1626.     thread's serial number. */
  1627. ThreadType ThreadBeginMain(ThreadProcType suspend, ThreadProcType resume,
  1628.     void *data)
  1629. {
  1630.     ThreadPtr thread; /* the new thread */
  1631.     Boolean success;    /* true if thread was created successfully */
  1632.  
  1633.     require(! gThread.main);
  1634.  
  1635.     /* This is always the first routine called for Thread Library,
  1636.         so this is a good place to do any one-time initializations
  1637.         of the library. Conversely, when the main thread is disposed
  1638.         of is a good time to do any final cleanup of the library. */
  1639.         
  1640.     thread = NULL;
  1641.     success = false;
  1642.     ThreadErrorSet(THREAD_ERROR_NONE);
  1643.  
  1644.     /* allocate thread structure */
  1645.     thread = PtrBeginClear(sizeof(ThreadStructure), THREAD_TYPE_THREAD);
  1646.     if (! thread && ! ThreadError())
  1647.         ThreadErrorSet(memFullErr);
  1648.     if (thread) {
  1649.     
  1650.         /* initialize thread structure */
  1651.         thread->fpu = ThreadHasFPU();
  1652.         thread->enabled = true;
  1653.         thread->main = true;
  1654.         thread->appl.suspend = suspend;
  1655.         thread->appl.resume = resume;
  1656.         thread->appl.data = data;
  1657.         thread->sn = ++gThread.lastsn;
  1658.         thread->stack.limit = LMGetApplLimit();
  1659.         thread->stack.top = LMGetCurStackBase();
  1660.         thread->stack.size = thread->stack.top - thread->stack.limit;
  1661.         
  1662.         /* save values of low-memory globals */
  1663.         thread->lm.heapEnd = LMGetHeapEnd();
  1664.         thread->lm.applLimit = LMGetApplLimit();
  1665.         thread->lm.hiHeapMark = LMGetHiHeapMark();
  1666.         
  1667.         /* now that the thread is ready to use, append it to the queue of 
  1668.             threads so that it can be scheduled for execution */
  1669.         ThreadEnqueue(&gThread.queue, thread);
  1670.         
  1671.         /* make this thread the active and main thread */
  1672.         gThread.active = thread;
  1673.         gThread.main = thread;
  1674.         
  1675.         /* we've successfully created the main thread */
  1676.         success = true;
  1677.     }
  1678.     
  1679.     /* cleanup if failed */
  1680.     if (! success && thread) {
  1681.         ThreadDispose(thread);
  1682.         thread = NULL;
  1683.     }
  1684.         
  1685.     ensure(thread ? ThreadValid(thread) && ! ThreadError() : ThreadError());
  1686.     ensure(thread == gThread.main);
  1687.     ensure(gThread.active == gThread.main);
  1688.     return(ThreadSN(thread));
  1689. }
  1690.  
  1691. /* called to start executing a new thread; must be a separate
  1692.     function to work on powerpc */
  1693. static void ThreadStart(void)
  1694. {
  1695.     /* call the thread's begin function */
  1696.     if (gThread.active->appl.begin)
  1697.         gThread.active->appl.begin(gThread.active->sn, gThread.active->appl.data);
  1698.         
  1699.     /* set up the thread's context */
  1700.     ThreadResume(gThread.active);
  1701.     
  1702.     /* call the thread's entry point */
  1703.     gThread.active->appl.entry(gThread.active->appl.data);
  1704.     
  1705.     /* dispose of the thread and switch to the next scheduled thread */
  1706.     ThreadEndPtr(gThread.active);
  1707.  
  1708.     ensure(false); /* never returns */
  1709. }
  1710.  
  1711. /*    ThreadBegin creates a new thread and returns the thread's serial number.
  1712.     You must create the main thread with ThreadBeginMain before you can call
  1713.     ThreadBegin. */
  1714. ThreadType ThreadBegin(ThreadProcType entry,
  1715.     ThreadProcType suspend,
  1716.     ThreadProcType resume,
  1717.     void *data, ThreadSizeType stack_size)
  1718. {
  1719.     ThreadPtr thread; /* new thread */
  1720.     Boolean success;    /* true if thread was created successfully */
  1721.     
  1722.     require(ThreadValid(gThread.main));
  1723.     require(entry != NULL);
  1724.     require(0 <= stack_size);
  1725.  
  1726.     /* clear results */
  1727.     thread = NULL;
  1728.     success = false;
  1729.     ThreadErrorSet(THREAD_ERROR_NONE);
  1730.  
  1731.     /* allocate thread structure */
  1732.     thread = PtrBeginClear(sizeof(ThreadStructure), THREAD_TYPE_THREAD);
  1733.     if (thread) {
  1734.     
  1735.         /* The main thread uses the application's regular stack, while
  1736.             nonrelocatable blocks are allocated to contain the stacks of
  1737.             all other threads. The thread's stack size is aligned to
  1738.             a double-word size (8 byte boundary) for compatability
  1739.             with the PowerPC architecture. */
  1740.         if (! stack_size)
  1741.             stack_size = ThreadStackDefault();
  1742.         stack_size += stack_size % 8;
  1743.         thread->stack.limit = PtrBegin(stack_size, THREAD_TYPE_STACK);
  1744.         if (thread->stack.limit) {
  1745.         
  1746.             /* initialize thread structure */
  1747.             thread->fpu = ThreadHasFPU();
  1748.             thread->enabled = true;
  1749.             thread->sn = ++gThread.lastsn;
  1750.             thread->appl.entry = entry;
  1751.             thread->appl.suspend = suspend;
  1752.             thread->appl.resume = resume;
  1753.             thread->appl.data = data;
  1754.             thread->stack.size = stack_size;
  1755.             thread->stack.top = thread->stack.limit + stack_size;
  1756.             
  1757.             /* Since all threads other than the main thread use stacks
  1758.                 allocated in the application's heap, we need to disable the
  1759.                 stack sniffer VBL task by setting the low-memory global
  1760.                 variable StkLowPt to 0. Otherwise, the stack sniffer would
  1761.                 generate system error #28. */
  1762.             LMSetStkLowPt(NULL);
  1763.             
  1764.             /* Certain low-memory globals divide the stack and heap.
  1765.                 We change these globals when a thread other than the
  1766.                 main thread is activated so that certain OS traps will
  1767.                 work correctly. */
  1768.             thread->lm.heapEnd = thread->stack.limit;
  1769.             thread->lm.applLimit = thread->stack.limit;
  1770.             thread->lm.hiHeapMark = thread->stack.limit;
  1771.  
  1772.             /* Set up the new thread's registers so that we'll jump
  1773.                 here when the thread is first activated. */
  1774.             if (thread->fpu)
  1775.                 ThreadRegistersFPSave(thread->registers.fp);
  1776.             if (ThreadRegistersGPSave(thread->registers.gp)) {
  1777.  
  1778.                 /* We're now executing the *new* thread (I know, it doesn't
  1779.                     look like it, but it's all due to the magic [hell?] of
  1780.                     non-local gotos and global variables). At this point,
  1781.                     the stack is empty, so we can't access any local variables.
  1782.                     All subsequent executions of the thread will go through the
  1783.                     ThreadRegistersSave call in ThreadActivate. */
  1784.                 ThreadStart();
  1785.                 check(false); /* never returns */
  1786.             }
  1787.  
  1788.             /*    Threads other than the main thread have their own private stack.
  1789.                 To be able to switch stacks, the first time the registers are saved
  1790.                 we need to set the stack pointer register that we saved in the
  1791.                 general purpose register array to the top of the thread's private
  1792.                 stack. We do this by knowing the index of the stack pointer in the
  1793.                 array of general purpose registers. For the M68K, we also need to
  1794.                 set the frame pointer to NULL. For the powerpc, we need to leave
  1795.                 space from the top of the stack for the linkage area. */
  1796.             #ifdef powerc
  1797.                 thread->registers.gp[REGISTER_SP] = (long) (thread->stack.top - 12);
  1798.             #else /* powerc */
  1799.                 thread->registers.gp[REGISTER_SP] = (long) thread->stack.top;
  1800.                 thread->registers.gp[REGISTER_FP] = 0;
  1801.             #endif /* powerc */
  1802.  
  1803.             /* now that the thread is ready to use, append it to the queue of
  1804.                 threads so that it can be scheduled for execution */
  1805.             ThreadEnqueue(&gThread.queue, thread);
  1806.             
  1807.             /* We've now successfully created a new thread and set things up so
  1808.                 that the first time the thread is invoked we'll call the thread's
  1809.                 entry point. We let the application call ThreadYield in its own
  1810.                 time to switch contexts. In other words, the new thread doesn't
  1811.                 start executing until it is scheduled to start or it is
  1812.                 specifically activated. */
  1813.             success = true;
  1814.         }
  1815.     }
  1816.     
  1817.     /* cleanup if failed */
  1818.     if (! success && thread) {
  1819.         ThreadDispose(thread);
  1820.         thread = NULL;
  1821.     }
  1822.     
  1823.     ensure(thread ? ThreadValid(thread) && ! ThreadError() : ThreadError());
  1824.     return(ThreadSN(thread));
  1825. }
  1826.